package com.zhouyu.paydemo.unipay.qrcode.HYM;

import com.zhouyu.paydemo.unipay.qrcode.DemoBase;
import com.zhouyu.paydemo.unipay.sdk.LogUtil;
import com.zhouyu.paydemo.unipay.sdk.QrcService;
import com.zhouyu.paydemo.unipay.sdk.SDKConfig;
import lombok.extern.slf4j.Slf4j;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.Map.Entry;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Servlet implementation class HymAcq
 */
@Slf4j
public class HymAcq extends HttpServlet {
	private static final long serialVersionUID = 1L;
       
    /**
     * @see HttpServlet#HttpServlet()
     */
    public HymAcq() {
        super();
    }

    final String encoding = "UTF-8";
	final String ACTION_FILL_ORDER = "fillOrder";
	final String ACTION_PUSH_ORDER = "pushOrder";
	final String ACTION_GET_AUTH = "getUserAuth";
	final String ACTION_FRONT_URL = "frontUrl";
	final String ACTION_FRONT_FAIL_URL = "frontFailUrl";
	final String ACTION_BACK_URL = "backUrl";
	
	final String GET_USR_AUTH_URL = "https://qr.95516.com/qrcGtwWeb-web/api/userAuth?version=1.0.0&redirectUrl=";
	
	private String frontUrl;
	private String backUrl;
	private String frontFailUrl;
	
	/**
	 * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
	 */
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//		response.getWriter().append("Served at: ").append(request.getContextPath());
//		response.getWriter().append("getRequestURI: ").append(request.getRequestURI());

		String urlPrefix = request.getRequestURL().toString() + "?action=";
		frontUrl = urlPrefix + ACTION_FRONT_URL; 
		backUrl = urlPrefix + ACTION_BACK_URL; 
		frontFailUrl = urlPrefix + ACTION_FRONT_FAIL_URL; 
		
		response.setCharacterEncoding("utf-8");
		response.setContentType("text/html; charset=utf-8");
		
		String ua = request.getHeader("user-agent");
		String uaReg = ".*(UnionPay/[^ ]+ ([0-9a-zA-Z]+)).*";
		
		String action = request.getParameter("action");
//		System.out.println(action);

		if(ua == null){
			forwordErr("非银联云闪付的user-agent。", request, response);
			return;
		}
		Pattern p = Pattern.compile(uaReg);
		Matcher m = p.matcher(ua);
		if(!m.matches()){
			forwordErr("非银联云闪付的user-agent。<br>ua为：<br>" + ua, request, response);
			return;
		}
		String appUpIdentifier = m.group(1);
//		System.out.println(appUpIdentifier);
		if (action == null) {
			//0. 填写订单信息。
			request.getRequestDispatcher("pages/hym/fillOrder.jsp").forward(request, response);
			return;
		} else if (ACTION_FILL_ORDER.equals(action)) {
			//1. 调获取用户授权。
			Map<String, String> params = getAllRequestParam(request);
			params.remove("action");
			String redirectUrl = urlPrefix + ACTION_GET_AUTH + "&" + getQueryString(params);
			response.sendRedirect( GET_USR_AUTH_URL + URLEncoder.encode(redirectUrl, "utf-8"));
//			System.out.println(GET_USR_AUTH_URL + URLEncoder.encode(redirectUrl, "utf-8"));
			return;
		} else if(ACTION_GET_AUTH.equals(action)){ 
			//2. 获取用户授权返回处理，没问题（00、34）的话，调获取用户标识接口，成功的话显示订单信息
			String userAuthCode = request.getParameter("userAuthCode");
			String respCode = request.getParameter("respCode");
			String acqCode = request.getParameter("acqCode");

			if(!"00".equals(respCode) && !"34".equals(respCode)){
				forwordErr("获取用户信息失败。respCode=" + respCode, request, response);
				return;
			}

			String userId = null;
			if(userAuthCode != null){
				// 调获取用户标识接口
				StringBuffer errMsgBuffer = new StringBuffer();
				userId = getUsrId(acqCode, userAuthCode, appUpIdentifier, errMsgBuffer);
				if (userId == null) {
					forwordErr("调用获取用户标识接口失败，失败信息：" + errMsgBuffer.toString(), request, response);
					return;
				}
				request.setAttribute("userId", userId);
				request.setAttribute("showUserId", userId);
			} else {
				request.setAttribute("showUserId", "游客");
			}
			
			Map<String, String> params = getAllRequestParam(request);
			for ( String key : params.keySet() ) {
				request.setAttribute(key,params.get(key));
			}
			params.put("action", ACTION_PUSH_ORDER);
			if(userId != null) params.put("userId", userId);
			String paramString = getQueryString(params);
			request.setAttribute("params", paramString);
			request.getRequestDispatcher("pages/hym/showOrder.jsp").forward(request, response);
			return;
		} else if(ACTION_PUSH_ORDER.equals(action)){ 
			//4. 触发订单推送接口，返回redirectUrl跳转我们前台网关。
			StringBuffer errMsgBuffer = new StringBuffer();
			String redirectUrl = orderPush(request, errMsgBuffer);
			if(redirectUrl == null) {
				forwordErr("调用订单推送接口失败，失败信息：" + errMsgBuffer.toString(), request, response);
				return;
			}
			response.sendRedirect(redirectUrl);
			return;
		} else if(ACTION_FRONT_FAIL_URL.equals(action)){ 
			//5. 失败前台通知
			forwordErr("交易失败，触发了frontFailUrl。", request, response);
			return;
		} else if(ACTION_FRONT_URL.equals(action)){ 
			//5. 前台通知
			doPostNotify(request, response);
			return;
		} else if(ACTION_BACK_URL.equals(action)){ 
			//5. 前台通知
			doPostNotify(request, response);
			return;
		} 
	}
	
	private String getQueryString(Map<String, String> params) throws UnsupportedEncodingException{
		StringBuffer sb = new StringBuffer();
		for ( String key : params.keySet() ) {
			sb.append(key + "=" + URLEncoder.encode(params.get(key), "utf-8") + "&");
		}
		sb.deleteCharAt(sb.length() - 1);
		return sb.toString();
	}
	
	private void forwordErr(String errMsg, HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{
		request.setAttribute("errInfo", errMsg);
		request.getRequestDispatcher("pages/hym/err.jsp").forward(request, response);
	}
	
	private String getUsrId(String acqCode, String userAuthCode, String appUpIdentifier, StringBuffer errMsg){

		Map<String, String> contentData = new HashMap<String, String>();

		contentData.put("version", "1.0.0");
		contentData.put("reqType", "0460000903");
		contentData.put("acqCode", acqCode); 
		contentData.put("userAuthCode", userAuthCode); 
		contentData.put("appUpIdentifier", appUpIdentifier); 
		
		Map<String, String> reqData = QrcService.sign(contentData,encoding);			 //报文中certId,signature的值是在signData方法中获取并自动赋值的，只要证书配置正确即可。
		String requestUrl = SDKConfig.getConfig().getQrcB2cMerBackTransUrl();
		Map<String, String> rspData = QrcService.post(reqData,requestUrl,encoding);  //发送请求报文并接受同步应答（默认连接超时时间30秒，读取返回结果超时时间30秒）;这里调用signData之后，调用submitUrl之前不能对submitFromData中的键值对做任何修改，如果修改会导致验签不通过
		
		if(!rspData.isEmpty()){
			if(QrcService.validate(rspData, encoding)){
				log.info("验证签名成功");
				String respCode = rspData.get("respCode");
				String respMsg = rspData.get("respMsg");
				String userId = rspData.get("userId");
				if(("00").equals(respCode)){
					return userId;
				} else {
					errMsg.append("respCode=" + respCode + ", respMsg=" + respMsg);
				}
			}else{
				log.error("验证签名失败");
				errMsg.append("验证签名失败");
			}
		}else{
			log.error("未获取到返回报文或返回http状态码非200");
			errMsg.append("未获取到返回报文或返回http状态码非200");
		}
		return null;
	}

	private String orderPush(HttpServletRequest request, StringBuffer errMsg){
		
		//TODO 获取对方ip，如果过了f5或者apache的话，这里可能要该从头里获取。如果获取不到可能还需要和系统室沟通把ip透传过来。
		String customerIp = request.getRemoteAddr();
		
		SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
		long time = new Date().getTime();
		String orderTime = sdf.format(time);
		String orderTimeOut = sdf.format(time + 2 * 60 * 1000);
		String currencyCode = request.getParameter("currencyCode");
		String areaInfo = request.getParameter("areaInfo");
		String frontUrl = this.frontUrl;
		String frontFailUrl = this.frontFailUrl;
		String backUrl = this.backUrl;
		String orderDesc = request.getParameter("orderDesc");
		
		String acqCode = request.getParameter("acqCode");
		String userId = request.getParameter("userId");
		String txnAmt = request.getParameter("txnAmt");
		String paymentValidTime = request.getParameter("paymentValidTime");
		String orderType = request.getParameter("orderType");
		String orderNo = request.getParameter("orderNo");
		String merId = request.getParameter("merId");
		String mcc = request.getParameter("mcc");
		String merName = request.getParameter("merName");
		String termId = request.getParameter("termId");
		String withFrontUrl = request.getParameter("withFrontUrl");
		
		Map<String, String> contentData = new HashMap<String, String>();

		contentData.put("version", "1.0.0");
		contentData.put("reqType", "0630001903");
		contentData.put("acqCode", acqCode); 
		contentData.put("txnAmt", txnAmt); 
		contentData.put("paymentValidTime", paymentValidTime); 
		contentData.put("orderTimeOut", orderTimeOut); 
		contentData.put("orderType", orderType); 
		contentData.put("orderTime", orderTime); 
		contentData.put("orderNo", orderNo); 
		contentData.put("orderDesc", orderDesc);  
		contentData.put("customerIp", customerIp); 
		contentData.put("currencyCode", currencyCode); 
		contentData.put("areaInfo", areaInfo); 
		if("true".equals(withFrontUrl)){
			contentData.put("frontUrl", frontUrl); 
			contentData.put("frontFailUrl", frontFailUrl);
		}
		contentData.put("backUrl", backUrl); 
		contentData.put("userId", userId); 

        Map<String, String> payeeInfoMap = new HashMap<String, String>(); 
		payeeInfoMap.put("id", merId); 
		payeeInfoMap.put("merCatCode", mcc); 
		payeeInfoMap.put("name", merName); 
		payeeInfoMap.put("termId", termId); 
		contentData.put("payeeInfo",DemoBase.getPayeeInfo(payeeInfoMap, encoding));
		
		Map<String, String> reqData = QrcService.sign(contentData, encoding);			 //报文中certId,signature的值是在signData方法中获取并自动赋值的，只要证书配置正确即可。
		String requestUrl = SDKConfig.getConfig().getQrcB2cMerBackTransUrl();
		Map<String, String> rspData = QrcService.post(reqData,requestUrl,encoding);  //发送请求报文并接受同步应答（默认连接超时时间30秒，读取返回结果超时时间30秒）;这里调用signData之后，调用submitUrl之前不能对submitFromData中的键值对做任何修改，如果修改会导致验签不通过
		
		if(!rspData.isEmpty()){
			if(QrcService.validate(rspData, encoding)){
				log.info("验证签名成功");
				String respCode = rspData.get("respCode");
				String respMsg = rspData.get("respMsg");
				String redirectUrl = rspData.get("redirectUrl");
				if(("00").equals(respCode)){
					return redirectUrl;
				} else {
					errMsg.append("respCode=" + respCode + ", respMsg=" + respMsg);
				}
			}else{
				log.error("验证签名失败");
				errMsg.append("验证签名失败");
			}
		}else{
			log.error("未获取到返回报文或返回http状态码非200");
			errMsg.append("未获取到返回报文或返回http状态码非200");
		}
		return null;
	}
	
	private void doPostNotify(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{

		String action = request.getParameter("action");
		log.info((ACTION_FRONT_URL.equals(action) ? "前台":"后台") + "接收报文返回开始");

		String pageResult = "pages/hym/utf8_result.jsp";
		
		Map<String, String> respParam = getAllRequestParam(request);
		respParam.remove("action");

		LogUtil.printRequestLog(respParam);

		Map<String, String> valideData = null;
		StringBuffer page = new StringBuffer();
		if (null != respParam && !respParam.isEmpty()) {
			Iterator<Entry<String, String>> it = respParam.entrySet()
					.iterator();
			valideData = new HashMap<String, String>(respParam.size());
			while (it.hasNext()) {
				Entry<String, String> e = it.next();
				String key = (String) e.getKey();
				String value = (String) e.getValue();
				page.append("<tr><td width=\"30%\" align=\"right\">" + key
						+ "(" + key + ")</td><td>" + value + "</td></tr>");
				valideData.put(key, value);
			}
		}
		if (!QrcService.validate(valideData, encoding)) {
			page.append("<tr><td width=\"30%\" align=\"right\">验证签名结果</td><td>失败</td></tr>");
			log.info("验证签名结果[失败].");
		} else {
			page.append("<tr><td width=\"30%\" align=\"right\">验证签名结果</td><td>成功</td></tr>");
			log.info("验证签名结果[成功].");
//			System.out.println(valideData.get("respCode")); //其他字段也可用类似方式获取
			
			@SuppressWarnings("unused")
			String respCode = valideData.get("respCode"); 
			//判断respCode=00后，对涉及资金类的交易，请再发起查询接口查询，确定交易成功后更新数据库。
		}
		
		request.setAttribute("result", page.toString());
		request.getRequestDispatcher(pageResult).forward(request, response);
		
		log.info((ACTION_FRONT_URL.equals(action) ? "前台":"后台") + "接收报文返回结束");
	}
	
	/**
	 * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
	 */
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doGet(request, response);
	}

	/**
	 * 获取请求参数中所有的信息
	 * 当商户上送frontUrl或backUrl地址中带有参数信息的时候，
	 * 这种方式会将url地址中的参数读到map中，会导多出来这些信息从而致验签失败，这个时候可以自行修改过滤掉url中的参数或者使用getAllRequestParamStream方法。
	 * @param request
	 * @return
	 */
	public static Map<String, String> getAllRequestParam(
			final HttpServletRequest request) {
		Map<String, String> res = new HashMap<String, String>();
		Enumeration<?> temp = request.getParameterNames();
		if (null != temp) {
			while (temp.hasMoreElements()) {
				String en = (String) temp.nextElement();
				String value = request.getParameter(en);
				res.put(en, value);
				// 在报文上送时，如果字段的值为空，则不上送<下面的处理为在获取所有参数数据时，判断若值为空，则删除这个字段>
				if (res.get(en) == null || "".equals(res.get(en))) {
					// System.out.println("======为空的字段名===="+en);
					res.remove(en);
				}
			}
		}
		return res;
	}
	
	public static void main(String[] args) {

		String ua = "Mobile Safari/ UnionPay/1.0 TESTPAY";
		String uaReg = ".*UnionPay/(1.0) ([0-9a-zA-Z]+).*";
		Pattern p = Pattern.compile(uaReg);
		Matcher m = p.matcher(ua);
		if(m.matches()){
			String appId = m.group(2);
			System.out.println(appId);
		} else {
			System.out.println("bb");
		}
	}

}
